
#include "ProgramElem.h"


#include "Type.h"

#include "Ast.h"					
#include "ParserUtil.h"					

/****************************************************************/
extern vector<three_addr_ins*> inter_code;
extern vector<string> *state_list;
string curState;
bool dfa_header = false;
extern const OpNode::OpInfo opInfo[] = {
  // print name, arity, paren_flag, fixity, arg types, out type, constraints
  //
  // Paren_flag -- opnode->print() outputs is surrounded by parenthesis if 
  // this flag is set. As set below, the expression may not print correctly
  // in some rare cases, e.g., ~(b * c) will get printed as ~b * c,
  // which actually corresponds to (~b)*c. To ensure that things get printed
  // correctly all the time, more paren_flags should be set to 1, but this
  // will lead to more clutter in printed output. Basically, what we have done
  // here is to look are expressions by type -- arithmetic, relational, 
  // boolean, bit operations, etc. Within each type, the highest priority 
  // operator is printed without paren. This will work correctly, as long
  // as the language doesn't permit mixing of different types of expressions.
  // But this assumption doesn't always hold, as in the example above. Also,
  // there is an exception to this general approach in the case of unary minus
  // and * -- since (-a)*b and -(a*b) have the same meaning, we can exclude
  // paren for * without an error.ru
  //
  // Codes for constraints:
  // first character:
  //    N: No additional constraint over what is given by argTypes
  //    I: all arguments must have identical type
  //    S: one of the arguments must have a type that is a supertype of
  //        of all other arguments. All other arguments require a coercion 
  //        operation to be introduced so as to convert their type to S.
  //    s: one of the arguments must have a type that is a subtype of
  //        of all other arguments. 
  //    L: all list arguments (and list output) must have same type. In 
  //        addition, all non-list arguments (and output) must have same 
  //        type as that of elements in these lists
  //    T: all tuple arguments to the function must have same type.
  //    A: (assignment). Type of second argument must be a subtype of
  //       the first argument
  //
  // second character:
  //    O: output type is the same as out type. (In the following cases,
  //        the output type need not be equal to out type, but a subtype
  //        of it.) Since a TypeTag provides complete type information only
  //        for primitive types, `O' is applicable only in this case.
  //    digit: output type is the same as that of the digit'th argument
  //       In this case, a third character may be used, the code for
  //       which is as follows:
  //         'e' denotes that the output is of type alpha, where
  //             the type of digit'th argument is list(alpha)
  //         'l' denotes that the output is of type list(alpha), where
  //             alpha is the type of the digit'th argument.
  //    S: The output type is the same as that of the argument with the
  //        most general type. (Typically used with first character 'S')
  //    s: The output type is the same as that of the argument with the
  //        least general type. (Typically used with first character 'S')
  //    P: The output type is the product of the types of all arguments
  //    p: The output type is a component of the input tuple type. The
  //        following character specifies the component. A digit k specifies
  //        that the component number as k. The character 'a' indicates that
  //        the component number is given by an integer argument to the
  //        operator. The argument number is given by the following digit.
  //        'p' can be used only in conjunction with first character 'P'.
  //    L: Output type is the same as type of list arguments. Can be used
  //        only in conjunction with first character L.
  //    e: Output type is the same as type of element of list arguments. 
  //        Can be used only in conjunction with first character L.
  //
  {OpNode::UMINUS, "-",  1, 0, OpNode::PREFIX, {Type::SIGNED}, Type::SIGNED, "N1"},
  {OpNode::PLUS, "+",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::MINUS, "-",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::MULT, "*",  2, 0, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::DIV, "/",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},  
  {OpNode::MOD, "%",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "S2"},
  {OpNode::EQ, "==", 2, 0, OpNode::INFIX, {Type::PRIMITIVE, Type::PRIMITIVE}, Type::BOOL, "SO"},
  {OpNode::NE, "!=", 2, 0, OpNode::INFIX, {Type::PRIMITIVE, Type::PRIMITIVE}, Type::BOOL, "SO"},
  {OpNode::GT, ">",  2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::LT, "<",  2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::GE, ">=", 2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::LE, "<=", 2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::AND, "&&",  2, 1, OpNode::INFIX, {Type::BOOL, Type::BOOL}, Type::BOOL, "NO"},
  {OpNode::OR, "||",  2, 1, OpNode::INFIX, {Type::BOOL, Type::BOOL}, Type::BOOL, "NO"},
  {OpNode::NOT, "!",  1, 0, OpNode::PREFIX, {Type::BOOL}, Type::BOOL, "NO"}, 
  {OpNode::BITNOT, "~",  1, 0, OpNode::PREFIX, {Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::BITAND, "&",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "Ss"},
  {OpNode::BITOR, "|",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "SS"},
  {OpNode::BITXOR, "^",  2, 0, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "SS"},
  {OpNode::SHL, "<<", 2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::SHR, ">>", 2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::ASSIGN, "=",  2, 0, OpNode::INFIX, {Type::NATIVE, Type::NATIVE}, Type::VOID, "AO"},
  {OpNode::PRINT, "print", OpNode::VARIABLE, 1, OpNode::PREFIX, {Type::NATIVE}, Type::VOID, "NO"},
  {OpNode::INVALID, "invalid",            0, 0, OpNode::PREFIX, {}, Type::ERROR, "NO"}
};

OpNode::OpNode(OpCode op, ExprNode* a1, ExprNode* a2, 
			   int ln, int col, string file):
  ExprNode(ExprNode::OP_NODE, NULL, ln,col,file) {
  opCode_ = op;
  if (a1 != NULL) {
	arity_ = 1;
        
	arg_.push_back(a1);
	if (a2 != NULL) {
	  arity_++;
	  arg_.push_back(a2);
	}
  }
  //william: check types of a1 and a2
  typeCheck();
  //aripio: addr initialize
  //addr_ = NULL;
  
}

OpNode::OpNode(const OpNode &other):
  ExprNode(other) {
  arity_ = other.arity();
  opCode_ = other.opCode();
  addr_ = other.addr();
  code_ = other.code();
  for (unsigned int i=0; (i < other.arity()); i++) {
    if (other.arg_[i]) {
      arg_.push_back((other.arg_[i])->clone());
    } 
	else {
      arg_.push_back(NULL);
    }
  }
}

void 
OpNode::print(ostream& os, int indent) const {
  if (opInfo[opCode_].prtType_ == OpNode::PREFIX) {
	os << opInfo[opCode_].name_;
	if (arity_ > 0) {
	  if (opInfo[opCode_].needParen_) 
		os << '(';
	  for (unsigned i=0; i < arity_-1; i++) {
		if (arg_[i])
		  arg_[i]->print(os, indent);
	    else os << "NULL";
		os << ", ";
	  }
      if (arg_[arity_-1])
		arg_[arity_-1]->print(os, indent);
	  else os << "NULL";
	  if (opInfo[opCode_].needParen_) 
		os << ") ";
	}
  }
  else if ((opInfo[opCode_].prtType_ == OpNode::INFIX) && (arity_ == 2)) {
	if (opInfo[opCode_].needParen_) 
	  os << "(";
	if(arg_[0])
	  arg_[0]->print(os, indent);
	else os << "NULL";
	os << opInfo[opCode_].name_; 
	if(arg_[1])
	  arg_[1]->print(os, indent);
	else os << "NULL";
	if (opInfo[opCode_].needParen_) 
	  os << ")";
  }
  else internalErr("Unhandled case in OpNnode::print");
}

/****************************************************************/

/*
 * Implement them
 */

const Type* OpNode::typeCheck() {

    Type *t1 = NULL, *t2 =NULL, *tmpType;
    Type *genType;
    
    if (arg_[0]) {
        t1 = this->arg_[0]->type();
    }
    if (arg_[1])
    {    
	if(arg_[1]->exprNodeType() == ExprNode::INV_NODE)	 
		t2 = new Type(((InvocationNode*)arg_[1])->symTabEntry()->type()->retType()->tag());
	else
		t2 = this->arg_[1]->type();
    }    
    if ((t1!=NULL && t1->tag() == Type::ERROR) || (t2!=NULL && t2->tag() == Type::ERROR)) {
        this->type(new Type(Type::ERROR));
        return new Type(Type::ERROR);
    }
//  cout<<t1->fullName()<<":"<<t2->fullName()<<endl;
    string opName = opInfo[opCode_].name_;
    
    if (this->arity_ == 1) {
        switch(opCode_) {
            case OpNode::UMINUS:
                if (t1->tag() != Type::INT)
                    this->coercedType(new Type(Type::INT));
                t1 = new Type(Type::INT);
                
                break;
            case OpNode::NOT:
                t1 = new Type(Type::BOOL);
                this->coercedType(t1);
                break;
            case OpNode::BITNOT:
                if (t1->tag()!= Type::UINT && t1->tag()!= Type::INT) {
                    string s = "Incompatible type for argument 1 for operator ";
                    errMsg(s + opName,line(),0,file().c_str() );
                    this->coercedType(new Type(Type::UINT));
                }
                //same type
                break;
            default:
                break;
        }
        this->type(t1);
        return t1;
    }
    if (this->arity_ == 2) {
        
        arg_[0]->coercedType(NULL);
        arg_[1]->coercedType(NULL);
        if (t1->tag() == t2->tag())
            if (t1->typeDesc()!=NULL) //for class
                this->type(new Type(t1->typeDesc(), t1->tag()));

        if (opCode_ == OpNode::ASSIGN) {
            this->type(new Type(t1->tag()));
            if (!t2->isSubType(*t1)) {
            	errMsg("Assignment between incompatible types",line(),0,file().c_str());
                this->type(new Type(Type::ERROR));
            }
            if (t1->tag() != t2->tag()) {
                genType = setCoercedType(t1, t2, arg_[0], arg_[1]);
                this->type(genType);
            }
        }
        else if (opCode_ == OpNode::BITAND || opCode_ == OpNode::BITOR)
        {
            this->type(new Type(Type::INT));
            if (t1->tag() != Type::INT) {
                string s = "Incompatible type for argument 1 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
            }
            if (t2->tag() != Type::INT) {
                string s = "Incompatible type for argument 2 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
            }
            if (t1->tag() != t2->tag()) {
                genType = setCoercedType(t1, t2, arg_[0], arg_[1]);
                this->type(genType);
            }
        }
        else if ( opCode_ == OpNode::PLUS || opCode_ == OpNode::MINUS || opCode_ == OpNode::MULT || 
                opCode_ == OpNode::DIV || opCode_ == OpNode::MOD)
        {// Arithmetci                        
            if (opCode_ == OpNode::MOD) {
                this->type(new Type(Type::INT));
                if (!(t1->tag() == Type::INT || t2->tag() == Type::INT || t1->tag() == Type::UINT || t2->tag() == Type::UINT)) {
                        string s = "Incompatible type for argument 1 for operator ";
                        errMsg(s + opName,line(),0,file().c_str() );
                        //this->type(new Type(Type::ERROR));
                }
                if (t1->tag() != t2->tag()) 
                    genType = setCoercedType(t1, t2, arg_[0], arg_[1]);

            } else {
                this->type(new Type(t1->tag()));
                if (!isIntFloat(t1)) {
                    string s = "Incompatible type for argument 1 for operator ";                 
                    errMsg(s + opName,line(),0,file().c_str() );
                    //this->type(new Type(Type::ERROR));
                }
                if (!isIntFloat(t2)) {
                    string s = "Incompatible type for argument 2 for operator ";
                    errMsg(s + opName,line(),0,file().c_str() );
                    //this->type(new Type(Type::ERROR));
                }
                if (t1->tag() != t2->tag()) {
                    genType = setCoercedType(t1, t2, arg_[0], arg_[1]);
                    this->type(genType);
                }
            }                
        }
        else if (opCode_ == OpNode::SHL || opCode_ == OpNode::SHR || opCode_ == OpNode::BITXOR) 
        {   // >>, <<, ^
            this->type(new Type(Type::INT));//?
            if (Type::isFloat(t1->tag())) {
                string s = "Incompatible type for argument 1 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
            }
            if (Type::isFloat(t2->tag())) {
                string s = "Incompatible type for argument 2 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
            }
            if (t1->tag() != t2->tag()) {
                genType = setCoercedType(t1, t2, arg_[0], arg_[1]);
                this->type(genType);
            }
        }
        else if (opCode_ == OpNode::NE || opCode_ == OpNode::EQ || opCode_ == OpNode::GT || opCode_ == OpNode::LT || opCode_ == OpNode::GE || opCode_ == OpNode::LE)
        { //Relational Operator takes either int or float, see spec
            this->type(new Type(Type::BOOL));
            if (!isIntFloat(t1)) {
                string s = "Incompatible type for argument 1 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
                //this->type(new Type(Type::ERROR));
            }
            if (!isIntFloat(t2)) {
                string s = "Incompatible type for argument 2 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
                //this->type(new Type(Type::ERROR));
            }
            if (t1->tag() != t2->tag()) {
                setCoercedType(t1, t2, arg_[0], arg_[1]);
            }
        }
        else if (opCode_ == OpNode::AND || opCode_ == OpNode::OR)
        {   //Logical Operator
            this->type(new Type(Type::BOOL));
            if (t1->tag() != Type::BOOL) {
                string s = "Incompatible type for argument 1 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
                //this->type(new Type(Type::ERROR));
            }
            if (t2->tag() != Type::BOOL) {
                string s = "Incompatible type for argument 2 for operator ";
                errMsg(s + opName,line(),0,file().c_str() );
                //this->type(new Type(Type::ERROR));
            } 
        } else {
            cout<<"------------ undefined operator -------------"<<opInfo[opCode_].name_<<endl;
        }        
        return type(); // -------- function return --------        
    }
}

void OpNode::typePrint(ostream& os, int indent) const {
    if (arity_ == 1) {
        if(this->coercedType() != NULL) {
            if (opCode_ == OpNode::UMINUS)
                os<<"-";

            os<<"(";
            os<<this->coercedType()->fullName();
            os<<")";
        }
        arg_[0]->typePrint(os, indent); //print valueNode
        
    }
    if (arity_ == 2) {
	//if (opInfo[opCode_].needParen_)
	//os << "(";
	if(arg_[0])
	  arg_[0]->typePrint(os, indent);
	else os << "NULL";
	os << opInfo[opCode_].name_;  //print operator
	if(arg_[1])
	  arg_[1]->typePrint(os, indent);
	else os << "NULL";
	//if (opInfo[opCode_].needParen_)
	//os << ")";
    }
    
	
}

string OpNode::addr() const
{
	return addr_;
}

string OpNode::code() const
{
	return code_;
}

address* OpNode::addr3() const
{
	return addr3_;
}

three_addr_ins* OpNode::code3() const
{
	return code3_;
}

void OpNode::genCode()
{
    if(opCode_ != OpNode::ASSIGN)
	 addr_ = gen_temp();
    if (this->arity_ == 1) {
	arg_[0]->genCode();
        switch(opCode_) {
            case OpNode::UMINUS:
               code_ = arg_[0]->code();
	       code_ += this->addr() + " = minus " + arg_[0]->addr()  + '\n';
               break;
            case OpNode::NOT:
               code_ = arg_[0]->code();
               code_ += this->addr() + " = neg " + arg_[0]->addr()  + '\n';
               break;
            case OpNode::BITNOT:
                //same type
                break;
            default:
                break;
        }
     }
    else if(this->arity_ == 2){
	
	arg_[0]->genCode();
	arg_[1]->genCode();
	switch(opCode_){
		case OpNode::PLUS:		 	
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code()  + this->addr() + " = " + arg_[0]->addr() + " + " + arg_[1]->addr() + '\n';
                  break;
		case OpNode::MINUS:		 
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " - " + arg_[1]->addr() + '\n';
                  break;
		case OpNode::MULT:
		
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " * " + arg_[1]->addr() + '\n';
                  break;
		case OpNode::DIV:		
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " / " + arg_[1]->addr()  + '\n';
                  break;
		case OpNode::MOD:		 
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " % " + arg_[1]->addr()  + '\n';
                  break;
		case OpNode::BITAND:		 
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " & " + arg_[1]->addr()  + '\n';
                  break;
		case OpNode::BITOR:		 
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " | " + arg_[1]->addr()  + '\n';
                  break;
		case OpNode::BITXOR:		 
		  code_ = arg_[0]->code();
	          code_ += arg_[1]->code() + this->addr() + " = " + arg_[0]->addr() + " ^ " + arg_[1]->addr()  + '\n';
                  break;
		case OpNode::ASSIGN:
		  code_ = arg_[1]->code();
	          code_ += arg_[0]->addr() + " = " + arg_[1]->addr();
                  break;

                //william: see P.404
                case OpNode::OR:
                    arg_[0]->set_true(this->true_);
                    arg_[0]->set_false(newLabel());
                    arg_[1]->set_true(this->true_);
                    arg_[1]->set_false(this->false_);
                    code_ = arg_[0]->code() + arg_[0]->false_lab() + arg_[1]->code();
                    break;
                }
        }
}

void OpNode::genCode3()
{
    code3_ = new three_addr_ins();
    addr3_ = new address();
    if(opCode_ != OpNode::ASSIGN)
    { 
	if(this->type()->tag() != Type::DOUBLE)        
            addr3_ -> atype = address::TEMP;
	else
            addr3_-> atype = address::TEMP_FLOAT;

	addr3_ -> temp = gen_temp();
        //string temp = gen_temp();
        //addr3_ -> temp = new char[temp.size()];
	//for(int i = 0; i < temp.size(); i++)
		//addr3_ ->temp[i] = temp[i];
    }
    else addr3_ -> atype = address::VOID;
    
    if (this->arity_ == 1) {
	//william: OpNode:NOT
        if (opCode_ == OpNode::NOT) {
            arg_[0]->set_true(this->false_lab());
            arg_[0]->set_false(this->true_lab());
            arg_[0]->genCode3();
            return; //--- end ---
        }

        arg_[0]->genCode3();
	code3_->arity = 1;
        code3_-> oprand1 = arg_[0]->addr3();
	code3_-> dest = addr3_;
	switch(opCode_) {
            case OpNode::UMINUS:
	       code3_->op = "NEG";
               break;            
	    //remove by wiliam
            /*case OpNode::NOT:
               code3_->op = "NOT";
               break;
            */
            case OpNode::BITNOT:
                code3_->op = "BITNOT";
                break;
            default:
                break;
        }
     }
    else if(this->arity_ == 2){
		
        if (opCode_ == OpNode::OR) {//william: Boolean            
            code3_->arity = 2;
            arg_[0]->set_true(this->true_lab());
            arg_[0]->set_false(newLabel());
            arg_[1]->set_true(this->true_lab()); //B2.true = B.true
            arg_[1]->set_false(this->false_lab());
            //genCode
            arg_[0]->genCode3();
            inter_code.push_back(genLabel(arg_[0]->false_lab()));
            arg_[1]->genCode3();
            return; // --- end ---
        }
        if (opCode_ == OpNode::AND) {
            code3_->arity = 2;
            arg_[0]->set_true(newLabel());
            arg_[0]->set_false(this->false_lab());
            arg_[1]->set_true(this->true_lab()); //B2.true = B.true
            arg_[1]->set_false(this->false_lab());
            //genCode
            arg_[0]->genCode3();
            inter_code.push_back(genLabel(arg_[0]->true_lab()));
            arg_[1]->genCode3();
            return; // --- end ---
        }
        if (isRelOp(opCode_)) {
            arg_[0]->genCode3();
            arg_[1]->genCode3();
            inter_code.push_back(genIfLabel(
                    arg_[0]->addr3(), opInfo[opCode_].name_, arg_[1]->addr3(), this->true_lab()));
            inter_code.push_back(genGotoLabel(this->false_lab()));
            return;
        }
        // ------------ william end here ------- //
        arg_[0]->genCode3();
	arg_[1]->genCode3();

	if(opCode_ != OpNode::ASSIGN)	
	{
		code3_->arity = 2;
        	code3_-> oprand1 = arg_[0]->addr3();
		code3_-> oprand2 = arg_[1]->addr3();
		code3_-> dest = addr3_;
	}
	else
	{
		code3_->arity = 1;
		code3_-> oprand1 = arg_[1] ->addr3();
		code3_->dest = arg_[0] -> addr3();
	}
	if(this->type()->tag() != Type::DOUBLE && this->type()->tag() != Type::STRING)
	{switch(opCode_){
		case OpNode::PLUS:		 	
		  code3_->op = "ADD";
                  break;
		case OpNode::MINUS:		 
		  code3_->op = "SUB";
                  break;
		case OpNode::MULT:
		  code3_->op = "MUL";
                  break;
		case OpNode::DIV:		
		  code3_->op = "DIV";
		  break;
		case OpNode::MOD:		 
		  code3_->op = "MOD";
                  break;
		case OpNode::BITAND:		 
		  code3_->op = "AND";
		case OpNode::BITOR:		 
		  code3_->op = "OR";
                  break;
		case OpNode::BITXOR:		 
		  code3_->op = "XOR";
                  break;
		case OpNode::ASSIGN:
		  code3_->op = "MOVI";
                  break;                
		}
	}
	else if(this->type()->tag() == Type::STRING)
	{
		switch(opCode_){
			case OpNode::ASSIGN:
		  code3_->op = "MOVS";
                  break; 
		}
	}
	else switch(opCode_){
		case OpNode::PLUS:		 	
		  code3_->op = "FADD";
                  break;
		case OpNode::MINUS:		 
		  code3_->op = "FSUB";
                  break;
		case OpNode::MULT:
		  code3_->op = "FMUL";
                  break;
		case OpNode::DIV:		
		  code3_->op = "FDIV";
		  break;
		/*case OpNode::MOD:		 
		  code3_->op = "MOD";
                  break;
		case OpNode::BITAND:		 
		  code3_->op = "BITAND";
		case OpNode::BITOR:		 
		  code3_->op = "BITOR";
                  break;
		case OpNode::BITXOR:		 
		  code3_->op = "BITXOR";
                  break;*/
		case OpNode::ASSIGN:
		  code3_->op = "MOVF";
                  break;                
		}
        }
	inter_code.push_back(code3_);

        
}
/****************************************************************/
AstNode::AstNode(NodeType nt, int line, int column, string file) :
	ProgramElem(NULL, line, column, file) {}
AstNode::AstNode(const AstNode& a) : ProgramElem(a) {}
/****************************************************************/
ExprNode::ExprNode(const ExprNode& en): AstNode(en) {}

ExprNode::ExprNode(ExprNodeType et, const Value* val, int line, int column, string file): AstNode(EXPR_NODE, line, column, file){

	exprType_ = et;
	val_ = val;
        //william
        true_ = (char *)malloc(128);
        false_= (char *)malloc(128);
}

void ExprNode::print(ostream& os, int indent) const
{
	val_->print(os,indent);
}
/****************************************************************/
void ValueNode::print(ostream& os, int indent)const 
{
	ExprNode::print(os,indent);
}

const Type* ValueNode::typeCheck() {
    
    return this->type();
}

void ValueNode::typePrint(ostream& os, int indent) const 
{
	//xiaohua hou
	if(this->coercedType())
	{
		os<<"(";
		os<<this->coercedType()->name();
		os<<")";
		os<<this->type()->name();
	}
	else
		os<<this->type()->name();
}

string ValueNode::addr() const
{
	if(value()->type()->tag() == Type::INT || value()->type()->tag() == Type::UINT)
		return itoa(value()->ival());
	else 
	if(value()->type()->tag() == Type::BOOL)
	{
		if(value()->bval())
			return "true";
		else 
			return "false";
	}
	else
	if(value()->type()->tag() == Type::STRING)
	{
		string temp = "\"";
		temp += value()->sval() + "\"";
		return temp;
	}
	else
	if(value()->type()->tag() == Type::DOUBLE)
		return itoa(value()->dval());
	else
	internalErr("Type not defined in ValueNode");

}

address* ValueNode::addr3() const
{
	return addr3_;
}
three_addr_ins * ValueNode::code3() const {    
    return code3_;
}
void ValueNode::genCode3()
{
	addr3_ = new address();
        code3_ = new three_addr_ins(); //william: P.404
	//addr3_->atype = address::CONST;
        if(value()->type()->tag() == Type::INT || value()->type()->tag() == Type::UINT)
	{
		addr3_->iValue = value()->ival();
		addr3_->atype = address::CONST_INT;
	}
	else 
	if(value()->type()->tag() == Type::BOOL)
	{
            code3_->arity = three_addr_ins::GOTOLABEL;
            if(value()->bval()) { //william
                code3_->label = this->true_lab();
                addr3_->bValue = true;
            }
            else {                           
                code3_->label = this->false_lab();
                addr3_->bValue = false;
            }
	    addr3_->atype = address::CONST_BOOL;
            inter_code.push_back(code3_);
        }
	else
	if(value()->type()->tag() == Type::STRING)
	{
		string temp = "\"";
		temp += value()->sval() + "\"";

		addr3_->sValue = temp;
		addr3_->atype = address::CONST_STR;
	}
	else
	if(value()->type()->tag() == Type::DOUBLE)
	{
		addr3_->dValue = value()->dval();
		addr3_->atype = address::CONST_DOUBLE;
	}
	else
	internalErr("Type not defined in ValueNode");
}

/****************************************************************/
const Type* ExprStmtNode::typeCheck()
{
	if (expr_ != NULL) { expr_->typeCheck();}
}
/****************************************************************/
RefExprNode::RefExprNode(string ext, const SymTabEntry* ste, int line, int column, string file)
:ExprNode(ExprNode::REF_EXPR_NODE, 0 , line,column,file)
{
	ext_ = ext;
	if(ste != NULL)
		sym_ = ste;
        // William
        //Type *t;
        /*
        SymTabEntry *ste2 = new SymTabEntry(*ste);
        if (ste->type()->typeDesc())
            t = new Type(ste2->type()->typeDesc(), ste2->type()->tag());
        else
            t = new Type(ste2->type()->tag());*/
        //type(ste->type());
        //this->type(ste->type());        
        
}

RefExprNode::RefExprNode(const RefExprNode& ren):ExprNode(ren){}

void RefExprNode::print(ostream& os, int indent)const
{
	os << ext_;
}

const Type* RefExprNode::typeCheck(){}

void RefExprNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	if(this->coercedType())
	{
		os<<"(";
		os<<this->coercedType()->name();
		os<<")";
	}
	//Ying, print CString cs2 = cs1;
	//SymTabEntry *ste = stm.lookUp(ext_);
	const SymTabEntry *ste = this->symTabEntry();
	if(ste){
		//ste->typePrint(os,-2);
		os<<ste->type()->name();
		if(ste->type()->tag()==Type::CLASS){
			os<<" ";
			os<<ste->type()->typeDesc()->name();
		}
	}
}

string RefExprNode::addr() const
{
	const SymTabEntry *ste = this->symTabEntry();
	if(ste){
		return ste->name();
	}
}

address* RefExprNode::addr3() const
{
	return addr3_;
}

void RefExprNode::genCode3()
{
	addr3_ = new address();
	addr3_->atype = address::VARI;
	addr3_-> ve = (VariableEntry*)this->symTabEntry();
}

/****************************************************************/  
const Type* CompoundStmtNode::typeCheck()
{
	//aripio
	list<StmtNode*>::iterator iter;
	if(stmts_ != NULL)
	{
		//os<<endl;
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->typeCheck();
			//if((*iter)->stmtNodeKind()!= StmtNode::IF)
			//os<<";"<<endl;
		}

	}
}

void CompoundStmtNode::printWithoutBraces(ostream& os, int indent) const
{
	list<StmtNode*>::iterator iter;

	if(stmts_ != NULL)
	{
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->print(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)			
			os<<";"<<endl;
		}

	}
}

void CompoundStmtNode::print(ostream& os, int indent) const
{
	os<<"{";
	list<StmtNode*>::iterator iter;
	
	if(stmts_ != NULL)
	{
		os<<endl;
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->print(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)
			os<<";"<<endl;
		}

	}

	os<<"}";
}

void  CompoundStmtNode::typePrintWithoutBraces(ostream& os, int indent) const
{
	list<StmtNode*>::iterator iter;

	if(stmts_ != NULL)
	{
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			os<<"    ";
			(*iter)->typePrint(os,indent);
			//(*iter)->print(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)
			os<<";"<<endl;
		}
	}
}

void  CompoundStmtNode::typePrint(ostream& os, int indent) const
{
	os<<" {";
	os<<endl;
	this->typePrintWithoutBraces(os,indent);

	os<<"}";
}

/*
void StmtNode::typePrint(ostream& os, int indent) const
{
	os<<"StmtNode ss";

}*/
void CompoundStmtNode::genCode3( )
{

    list<StmtNode*>::iterator iter;
    if(stmts()!= NULL)
    {
	for(iter=this->stmts()->begin();iter!=this->stmts()->end();iter++){
        //william: add anything?
        (*iter)->genCode3();
    }
    }

}
/****************************************************************/ 
InvocationNode::InvocationNode(const SymTabEntry * ste, vector<ExprNode*>* param, int line, int column, string file):ExprNode(ExprNode::INV_NODE,0, line,column, file)
{
	this->params(param);
	ste_ = ste;
	//typeCheck();	
} 

void InvocationNode::print(ostream& os, int indent) const
{
	os<<this->symTabEntry()->name()<<"(";

	vector<ExprNode*>::iterator iter;
	int i = 0;
	if(params_ != NULL)
	{
	for(iter = params_->begin(); iter != params_->end(); iter++)
	{
		(*iter)->print(os,indent);
		i++;
		if(i != params_->size())
		os<<",";
	}
	}
	os<<")";
	
}
InvocationNode::InvocationNode(const InvocationNode& in): ExprNode(in)
{
} 

const Type* InvocationNode::typeCheck()
{	//aripio
	vector<ExprNode*>::iterator iter;
	vector<const Type*>::const_iterator iter2;
	if(this->symTabEntry()->name() == "print")
		return new Type(Type::VOID);	
	int i = 1;
	if(params_ != NULL)
	{
		if(params_->size() != this->symTabEntry()->type()->argTypes()->size())
		{
			string s = itoa(this->symTabEntry()->type()->argTypes()->size());
			s += " arguments expected for ";
			s += this->symTabEntry()->name();
			errMsg(s,line(),0,file().c_str());
			
		}
	else		
	for(iter = params_->begin(),iter2 = this->symTabEntry()->type()->argTypes()->begin(); 
		iter != params_->end(),iter2 != this->symTabEntry()->type()->argTypes()->end(); iter++,iter2++,i++)
	{
		if(!(*iter)->type()->isSubType((*iter2)->tag()) && (*iter)->type()->tag() != (*iter2)->tag())
		{
			string s = "Type mismatch for argument ";
			s += itoa(i) + " to " + this->symTabEntry()->name();
			errMsg(s,line(),0,file().c_str());
		}
		else
		if((*iter)->type()->tag() != (*iter2)->tag())
		{
			 (*iter)->coercedType((*iter2));
		}
	}
	}
}

void InvocationNode::typePrint(ostream& os, int indent) const
{

	os<<this->symTabEntry()->name()<<"(";

	vector<ExprNode*>::iterator iter;
	int i = 0;
	if(params_ != NULL)
	{
	for(iter = params_->begin(); iter != params_->end(); iter++)
	{
		(*iter)->typePrint(os,indent);
		i++;
		if(i != params_->size())
		os<<", ";
	}
	}
	os<<")";
}

address* InvocationNode::addr3() const{

	return addr_ ;

}

three_addr_ins* InvocationNode::code3() const{

	return code_;
}

void InvocationNode::genCode3(){
	
	//Ying
	code_=new three_addr_ins ();
	addr_= new address ();



	vector<ExprNode*>::reverse_iterator iter;

	if(this->symTabEntry()->name() == "print")
	{
		iter = this->params_->rbegin();
		switch((*iter)->exprNodeType())
		{
			case ExprNode::REF_EXPR_NODE:
				code_ -> oprand1 = new address();
				code_ -> oprand1->atype = address::VARI;
 				code_ -> arity = 1;
				code_ -> oprand1 -> ve = (VariableEntry*) (((RefExprNode*)(*iter))->symTabEntry());
				if(((RefExprNode*)(*iter))->symTabEntry()->type()->tag() != Type::DOUBLE && ((RefExprNode*)(*iter))->symTabEntry()->type()->tag() != Type::STRING)
				{
					code_->op = "PRTI";
					
				}
				else if(((RefExprNode*)(*iter))->symTabEntry()->type()-> tag() == Type::DOUBLE)code_->op = "PRTF";
				else code_->op = "PRTS";
				break;
			case ExprNode::VALUE_NODE:
				code_ -> oprand1 = new address();
				code_ -> arity = 1;
				(*iter)->genCode3();
				code_ -> oprand1 = (*iter)->addr3();
				if((*iter)->type()->tag()!= Type::DOUBLE && (*iter)->type()->tag()!=Type::STRING)
				
					code_->op = "PRTI";
					
				else if((*iter)->type()->tag()== Type::DOUBLE)
				code_->op = "PRTF";
				else code_->op = "PRTS";
				break;
		}
		inter_code.push_back(code_);
		return;
	}

	three_addr_ins * temp = new three_addr_ins();
	temp -> oprand1 = new address();
	temp -> dest = new address();
 	temp -> arity = 1;
	temp -> op = "MOVI";
	temp -> oprand1 -> atype = address::CONST_INT;
	temp -> oprand1 -> iValue = 10000;
	temp -> dest -> atype = address::REG;
	temp -> dest -> reg = "R900";	
	inter_code.push_back(temp);
	if(params_ != NULL)
	{for(iter=this->params_->rbegin();iter!=params_->rend();iter++ ){

		three_addr_ins *param = new three_addr_ins ();

		(*iter)->genCode3();
		param->oprand1 = (*iter)->addr3() ;
		/*if((*iter)->exprNodeType()==ExprNode::VALUE_NODE){
			param->oprand1->atype=address::CONST;
		}

		else if ((*iter)->exprNodeType()==ExprNode::OP_NODE){
			param->oprand1->atype=address::TEMP;
		}

		else {
			param->oprand1->atype=address::VARI;
		}*/

		param->op="PARAM";
		param->arity = 1;
		param->dest = NULL;
		inter_code.push_back(param);
	}
	}
	addr_->temp= gen_temp();
	addr_->atype=address::TEMP;

	code_->dest= addr_;

	code_->op="CALL";
	code_->oprand1 = new address (); //function name
	code_->oprand2 = new address (); //number os args
	code_->dest = new address();
	code_->oprand1->reg = this->symTabEntry()->name();
	code_->oprand1->atype=address::REG; //should be modified after changing address class
	code_->oprand2->iValue = this->arity();
	code_->oprand2->atype=address::CONST;
	code_->arity = three_addr_ins::CALL;

	code_->oprand1->fe = (FunctionEntry*)this->symTabEntry();

	code_->dest = addr_;

	inter_code.push_back(code_);
}

/*void InvocationNode::genCode(){

	cout<<"invocationGenCode3";
	vector<ExprNode*>::iterator iter;
	addr_ = gen_temp();

	int i = 0;
	for(iter=this->params_->begin();iter!=params_->end();iter++ ){
		code_ += "param " +(*iter)->addr() + "\n";
		i++;
	}

	code_+= addr() + " = " + "call " + this->ste_->name()+ ", " + itoa(i) + "\n";

}*/


/****************************************************************/ 
RuleNode::RuleNode(BlockEntry* re, BasePatNode * pat, StmtNode* reaction, int line, int column, string file):AstNode(AstNode::RULE_NODE, line, column, file)
{
	rste_ = re;
	pat_ = pat;
	reaction_ = reaction;	
}

void RuleNode::print(ostream& os, int indent)const
{

	this->pat()->print(os,indent);
	os<<"-->";
	if(this->reaction() != NULL)
	{
		this->reaction()->print(os, indent);
		if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			os<<";"<<endl;
	}
	os<<";;";
}

const Type* RuleNode::typeCheck()
{
	//aripio
	this->pat()->typeCheck();
	//os<<"--> ";
        if(this->reaction() != NULL)
	{
		this->reaction()->typeCheck();
		//if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			//os<<";"<<endl;
	}
	//cout<<";;";
}

void  RuleNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	this->pat()->typePrint(os,indent);
	os<<"--> ";
	if(this->reaction() != NULL)
	{
		this->reaction()->typePrint(os, indent);
		if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			os<<";"<<endl;
	}
	os<<";;";
}

void RuleNode::genCode()
{
	if(this->reaction() != NULL)
	{
		//cout<<reaction_->type()->name();;
		reaction_->genCode();
		cout<<reaction_->code();
         
	}
        
}

void RuleNode::genCode3()
{
    //william
    string newlab = newLabel();
    this->set_next(newLabel());
    pat_->set_true(newLabel());    
    pat_->set_false(this->next_lab());

    reaction_->set_next(this->next_lab());

    curState = newState();
    if (!dfa_header) { //gen DFA header
        three_addr_ins *tmp = new three_addr_ins();
        tmp->arity = three_addr_ins::DFA_HEADER;
        inter_code.push_back(tmp);
        dfa_header = true;
    }
    inter_code.push_back(genStateLabel(curState, "-1", this->pat_->false_lab()));   //-1: this dfa dead
    inter_code.push_back(genStateLabel(curState, "0", (char *)newlab.c_str()));     //0: initial state
    //inter_code.push_back(genStateLabel(curState, "0", pat_->start_lab()));     //0: initial state
    inter_code.push_back(genGotoLabel(curState));
    inter_code.push_back(genLabel(newlab));
    //genCode
    pat_->genCode3();
    inter_code.push_back(genLabel(pat_->true_lab()));
    if(this->reaction() != NULL)
            reaction_->genCode3();
    inter_code.push_back(genLabel(this->next_lab()));    
    if(!this->pat_->hasSeqOps()) {        
        inter_code.push_back(genUpdateStateLabel("-1", "MOVI"));
    }

}
/****************************************************************/ 
PatNode::PatNode(int line, int column, string file): BasePatNode(BasePatNode::PRIMITIVE,line,column,file){}

PatNode::PatNode(PatNodeKind pk, BasePatNode *p1, BasePatNode* p2, int line, int column, string file): BasePatNode(pk, line,column,file)
{
	this->kind(pk);
	pat1_ = p1;
	pat2_ = p2;        
        //this->typeCheck(); //william
}

bool PatNode::hasNeg() const
{
	return true;
}

bool PatNode::hasSeqOps() const
{
    //william 1.1.4
    bool pat1 = false, pat2 = false;
    if (this->kind() == BasePatNode::SEQ ||this->kind() == BasePatNode::STAR) {
        return true;
    }
    if (this->pat1_ != NULL) {
        pat1 = this->pat1_->hasSeqOps();
    }
    if (this->pat2_ != NULL) {
        pat2 = this->pat2_->hasSeqOps();
    }
    return (pat1 || pat2);
}

bool PatNode::hasAnyOrOther() const
{
	return true;
}

void PatNode::print(ostream& os, int indent)const
{
	os<<"(";
	if(this->kind() == BasePatNode::NEG)
		os<<"!";
	pat1_->print(os, indent);
	if(this->kind() == BasePatNode::STAR)
		os<<"**";
	if(pat2_ != NULL)
	{
	  switch(this->kind())
	 {
		case BasePatNode::SEQ: os<<":"; break;
		case BasePatNode::OR: os<<"\\/"; break; 
	 }
	  pat2_->print(os,indent);
	}
	os<<")";
}

const Type* PatNode::typeCheck(){
    //cout<<"PatNode tyep check!"<<endl;
    //william: 1.1.4    
    
    if (this->kind() == BasePatNode::NEG) {
        if (pat1_!= NULL && pat1_->hasSeqOps()) {
            errMsg("Invalid use of pattern negation",line(),0,file().c_str()); //see out07
            return new Type(Type::ERROR);
        }
        /*
        if (pat2_!= NULL && pat2_->hasSeqOps()) {
            errMsg("Invalid use of pattern negation",line(),0,file().c_str()); //see out07
            return new Type(Type::ERROR);
        }
    
         */
    }
    if (pat1_ != NULL)
        pat1_->typeCheck();
    if (pat2_!= NULL)
        pat2_->typeCheck();
    
    
    return new Type(Type::VOID);
    
}

void PatNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	os<<"(";
	if(this->kind() == BasePatNode::NEG)
		os<<"!";
        pat1_->typePrint(os, indent);
	if(this->kind() == BasePatNode::STAR)
		os<<"**";
	if(pat2_ != NULL)
	{
	  switch(this->kind())
	 {
		case BasePatNode::SEQ: os<<":"; break;
		case BasePatNode::OR: os<<"\\/"; break;
	 }
	  pat2_->typePrint(os,indent);
	}
	os<<")";
}
void PatNode::genCode3() {
    //william
    string newlab1;
    string newlab2;

    if (pat2_ == NULL) { //One PrimitivePatNode
        switch(this->patKind_) {
            case BasePatNode::PRIMITIVE:                                
                //state info
                pat1_->set_true(this->true_lab());
                pat1_->set_false(this->false_lab());
                // similar to IfNode
                pat1_->genCode3();
                break;
            case BasePatNode::NEG:                
                pat1_->set_true(this->false_lab());
                pat1_->set_false(this->true_lab());
                pat1_->genCode3();
                break;
            case BasePatNode::STAR:
                //state info
                newlab1= newLabel();
                newlab2= newLabel();
                
                pat1_->set_true(newlab2);
                pat1_->set_false(this->false_lab());
                
                inter_code.push_back(genLabel(newlab1));
                pat1_->genCode3();
                inter_code.push_back(genLabel(newlab2));
                inter_code.push_back(genUpdateStateLabel(newlab1, "MOVL"));
                inter_code.push_back(genGotoLabel(this->true_lab()));
                
                //cout<<"--------"<<pat1_->true_lab()<<endl;
                break;
        }
    }
    else
    {
        switch(this->patKind_) {
            case BasePatNode::OR:
                
                pat1_->set_true(this->true_lab());
                pat1_->set_false(newLabel());
                pat2_->set_true(this->true_lab());
                pat2_->set_false(newLabel());

                //genCode
                pat1_->genCode3();
                inter_code.push_back(genLabel(pat1_->false_lab()));
                pat2_->genCode3();
                inter_code.push_back(genLabel(pat2_->false_lab()));
                inter_code.push_back(genUpdateStateLabel("-1", "MOVI"));
                inter_code.push_back(genGotoLabel(this->false_lab()));

                break;
            case BasePatNode::SEQ:

                newlab2 = newLabel();

                pat1_->set_true(newLabel());
                pat1_->set_false(newLabel());
                pat2_->set_true(this->true_lab());
                pat2_->set_false(pat1_->false_lab());

                pat1_->genCode3();
                inter_code.push_back(genLabel(pat1_->true_lab()));
                inter_code.push_back(genUpdateStateLabel(newlab2, "MOVL"));
                inter_code.push_back(genGotoLabel(this->false_lab()));
                inter_code.push_back(genLabel(newlab2));
                pat2_->genCode3();
                inter_code.push_back(genLabel(pat1_->false_lab()));
                inter_code.push_back(genUpdateStateLabel("-1", "MOVI"));
                inter_code.push_back(genGotoLabel(this->false_lab()));
                
                break;
            default:
                cout<<"---- not defined patKind ----"<<endl;
                break;
        }
    }
    
}
/****************************************************************/ 
PrimitivePatNode::PrimitivePatNode(EventEntry* ee, vector<VariableEntry*>* params, ExprNode* c, int line, int column, string file): BasePatNode(BasePatNode::PRIMITIVE, line, column,file)
{
	ee_ = ee;
	params_ = params;
	cond_ = c;
        eventName_ = ee_->name();        
}

bool PrimitivePatNode::hasNeg() const
{
	return false;
}

bool PrimitivePatNode::hasSeqOps() const
{
	return false;
}

bool PrimitivePatNode::hasAnyOrOther() const
{
	return true;
}

void PrimitivePatNode::print(ostream& os, int indent)const
{
	os<<ee_->name();
	vector<const VariableEntry*>::const_iterator iter;
	int i = 0;
	if(this->event()->name() != "any")
{	os<<"(";
	if(this->params() != NULL)
{
	
	for(iter = this->params()->begin(); iter != this->params()->end(); iter++)
	{
		os<<(*iter)->type()->name()<<" ";
		if((*iter)->type()->tag() == Type::CLASS)
			os<<(*iter)->type()->typeDesc()->name()<<" ";
		os<<(*iter)->name();
		i++;
		if(i != params_->size())
		os<<",";
	}
	
}
	os<<")";
}
	if(this->cond() != NULL)
	{
		cout<<"|";
		this->cond()->print(os,indent);
	}
	
}

const Type* PrimitivePatNode::typeCheck(){
    //william
   
    vector<Type*>* te = ee_->type()->argTypes();
    vector<VariableEntry*>::const_iterator iter;
    if (te == NULL)
        return new Type(Type::VOID);
    vector<Type*>::iterator iter2 = te->begin();
    if (params()== NULL || te->size() != this->params()->size()) {
        string s = "Event " + ee_->name() + " requires " + itoa(te->size()) + " arguments";
        errMsg(s,line(),0,file().c_str());
        return new Type(Type::ERROR);
    }
    for(iter = this->params()->begin(); iter != this->params()->end(); iter++, iter2++)
    {
        Type *actual = (*iter)->type();
        Type *def = (*iter2);
        
        //cout<<actual->name()<<":"<<def->name()<<endl;
        if (!actual->isSubType(def->tag())) {
            if (actual->tag() != Type::UNKNOWN) {
                string s = "Incompatible type for event " + this->eventName_ + "\'s argument " + (*iter)->name();
                errMsg(s, line(),0,file().c_str());
                return new Type(Type::ERROR);
            }
        }
    }
    //this->cond_ ->typeCheck();
    return new Type(Type::VOID);
}

void PrimitivePatNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	//os<<"PrimitivePatNodePrint: ";
	os<<ee_->name();
	vector<const VariableEntry*>::const_iterator iter;
	int i = 0;

	if(this->event()->name() != "any")
	{
		os<<"(";
		if(this->params() != NULL && this->params()->size() != 0){
			//os<<(params_)->size();
			for(iter = this->params()->begin(); iter != this->params()->end(); iter++){
				(*iter)->typePrint(os,0);
				//os<<(*iter)->type()->name()<<" ";
				//if((*iter)->type()->tag() == Type::CLASS)
					//os<<(*iter)->type()->typeDesc()->name()<<" ";
				//os<<(*iter)->name();
				i++;
				if(i != params_->size())
					os<<", ";
			}
		}
		os<<")";
	}

	if(this->cond() != NULL)
	{
		cout<<"|";
		this->cond()->typePrint(os,indent);
	}
}

void PrimitivePatNode::genCode3(){
    //william    
    string eventName = this->ee_->name();
    
    //inter_code.push_back(genRuleLabel(eventName, this->state_lab()));
    //inter_code.push_back(genIfLabel(eventName, this->true_lab()));
    string eventIntName = itoa((int)((char)eventName.at(0)));
    inter_code.push_back(genRuleLabel(eventIntName, this->true_lab()));
    inter_code.push_back(genGotoLabel(this->false_lab()));
    //inter_code.push_back(genNextState(state));
    //ee_->set_false(this->next_lab());
    //inter_code.push_back(genLabel(cond_->true_lab()));
    return;
}

/****************************************************************/ 
IfNode::IfNode(ExprNode* cond, StmtNode* thenStmt, StmtNode* elseStmt, int line, int column, string file):StmtNode(StmtNode::IF,line, column, file)
{
	cond_ = cond;
	then_ = thenStmt;
	else_ = elseStmt;	
        //this->typeCheck();
}

void IfNode::print(ostream& os, int indent) const
{
	os<<"if(";
	cond_->print(os,indent);
	os<<") ";
	if(this-> thenStmt() != NULL)	
	{
		this -> thenStmt()->print(os,indent);
		if(this->thenStmt()->stmtNodeKind() == StmtNode::EXPR|| this->thenStmt()-> stmtNodeKind() == StmtNode::COMPOUND)		
		os<<";"<<endl;
		
	}
	if(this->elseStmt() != NULL)
	{	
		os<<"else ";
		this->elseStmt()->print(os,indent);
		if(this->elseStmt()->stmtNodeKind() == StmtNode::EXPR || this->elseStmt()-> stmtNodeKind() == StmtNode::COMPOUND)		
		os<<";"<<endl;				
	}
} 

const Type* IfNode::typeCheck() {
    //william: for if-then-else statement, condition must be boolean
    if (cond_ != NULL) {
        if (cond_->type() != NULL && cond_->type()->tag()!= Type::BOOL && !isIntFloat(cond_->type())) {
            //cout<<cond_->type()->fullName()<<cond_->type()->tag()<<Type::BOOL<<endl;
            errMsg("if-then-else statement, condition must be boolean",line(),0,file().c_str());
            return new Type(Type::ERROR);
        }
    }
    return new Type(Type::VOID);
}
  
void  IfNode::typePrint(ostream& os, int indent) const
{
    os<<"if(";
    cond_->typePrint(os,indent);
    os<<") ";
    if(this-> thenStmt() != NULL)
    {
            this -> thenStmt()->typePrint(os,indent);
            if(this->thenStmt()->stmtNodeKind() == StmtNode::EXPR|| this->thenStmt()-> stmtNodeKind() == StmtNode::COMPOUND)
            os<<";"<<endl;

    }
    if(this->elseStmt() != NULL)
    {
            os<<"else ";
            this->elseStmt()->typePrint(os,indent);
            if(this->elseStmt()->stmtNodeKind() == StmtNode::EXPR || this->elseStmt()-> stmtNodeKind() == StmtNode::COMPOUND)
            os<<";"<<endl;
    }

}
//william
three_addr_ins* IfNode::code3() const
{
	return code3_;
}
 
void IfNode::genCode3() { //william: P.402 textbook

    code3_ = new three_addr_ins(); //do we need this?
    //P -> S
    this->set_next(newLabel());
    
    StmtNode *s1 = this->thenStmt();
    StmtNode *s2 = this->elseStmt();
    
    if (this->elseStmt() == NULL) {//S -> if(B) S1
        if (s1 == NULL) {
            cond_->set_true(this->next_lab());
        } else {
            cond_->set_true(newLabel());        //B.true = newlabel
            s1->set_next(this->next_lab());     //S1.next = S.next
        }
        cond_->set_false(this->next_lab()); //B.false = S.next
        //genCode
        cond_->genCode3();        
        inter_code.push_back(genLabel(cond_->true_lab()));
        if (s1!= NULL)
            s1->genCode3();
        inter_code.push_back(genLabel(this->next_lab()));
    }    
    else {  //S -> if (B) S1 else S2
        
        cond_->set_true(newLabel());  //B.true = newlabel
        cond_->set_false(newLabel()); //B.false = newlabel
        s1->set_next(this->next_lab()); //S1.next = S.next
        s2->set_next(this->next_lab()); //S2.next = S.next
        //genCode
        cond_->genCode3();  //B.code
        inter_code.push_back(genLabel(cond_->true_lab())); //label(B.true)
        s1->genCode3();
        inter_code.push_back(genGotoLabel(this->next_lab()));        //gen(goto S.next)
        inter_code.push_back(genLabel(cond_->false_lab())); //label(B.false)
        if (!s2)
            s2->genCode3(); //S2.code
        inter_code.push_back(genLabel(this->next_lab()));
    }

}
/****************************************************************/ 
const Type* ReturnStmtNode::typeCheck(){
    //william: the value being returned is a subtype of the function return type
    if (fun_ == NULL)
        errMsg("ReturnStmtNode: No fun_",line(),0,file().c_str());
    if (expr_ == NULL)
        errMsg("ReturnStmtNode: No return expr",line(),0,file().c_str());
	//cout<<fun_->name()<<endl;
    const Type *funRetType = fun_->type()->retType();
    Type *retType = expr_->type();
	//cout<<funRetType->fullName()<<endl;
    if(funRetType->tag() == Type::VOID)
	errMsg("No return value expected for a void function",line(),0,file().c_str());	
    else
    if (!retType->isSubType(*funRetType)) {
        errMsg("Return value incompatible with current function's type",line(),0,file().c_str());
        return new Type(Type::ERROR);
    }
    return new Type(Type::VOID);
}
/****************************************************************/ 


void ReturnStmtNode::genCode3(){

	this->expr_->genCode3();

	addr_ = new address();
	code_ = new three_addr_ins();

	//addr_=this->expr_->addr3();
	code_->op="RETURN";
	code_->arity=1;
	code_->oprand1=this->expr_->addr3();
	inter_code.push_back(code_);

}
